// SunCode MVP encoder/decoder in polar layout
// Versioned ring/sector symbol with masking similar to QR

function defaultParams(version = 1) {
	const rings = 6 + 2 * (version - 1); // R
	const sectors = (r) => 32 + 4 * r; // S(r)
	return { version, rings, sectors, ecLevel: 'M' };
}

function toBitArray(uint8) {
	const bits = [];
	for (let i = 0; i < uint8.length; i += 1) {
		const b = uint8[i];
		for (let k = 7; k >= 0; k -= 1) bits.push((b >> k) & 1);
	}
	return bits;
}

function fromBitArray(bits) {
	const bytes = [];
	for (let i = 0; i < bits.length; i += 8) {
		let v = 0;
		for (let k = 0; k < 8 && i + k < bits.length; k += 1) v = (v << 1) | (bits[i + k] & 1);
		bytes.push(v);
	}
	return new Uint8Array(bytes);
}

function encodeDataBytes(dataStr) {
	// Simple UTF-8 bytes
	const enc = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
	const bytes = enc ? enc.encode(dataStr) : _polyfillUtf8Encode(dataStr);
	// Header: mode=byte(0b0100), len (16 bits)
	const header = [];
	// mode 4 bits
	header.push(0,1,0,0);
	// length 16 bits
	const len = bytes.length;
	for (let k = 15; k >= 0; k -= 1) header.push((len >> k) & 1);
	const payload = toBitArray(bytes);
	let bits = header.concat(payload);
	// terminator up to 4 bits
	const cap = estimateCapacityBits(1); // provisional; will be resized by version search
	const remaining = Math.max(0, 4 - (bits.length % 8));
	for (let i = 0; i < remaining; i += 1) bits.push(0);
	// pad bytes 0xEC, 0x11
	const pads = [0xEC, 0x11];
	let bi = 0;
	while (bits.length % 8 !== 0) bits.push(0);
	let padIdx = 0;
	while (bits.length < cap) {
		const v = pads[padIdx % 2];
		padIdx += 1;
		for (let k = 7; k >= 0; k -= 1) bits.push((v >> k) & 1);
	}
	return bits;
}

function estimateCapacityBits(version) {
	const { rings, sectors } = defaultParams(version);
	let usable = 0;
	for (let r = 0; r < rings; r += 1) usable += sectors(r);
	// Reserve for core/timing/alignment/format ≈ 10% as rough
	usable = Math.floor(usable * 0.9);
	return usable;
}

function chooseVersion(bitsNeeded) {
	for (let v = 1; v <= 10; v += 1) { // limit MVP to 10
		if (estimateCapacityBits(v) >= bitsNeeded) return v;
	}
	return 10;
}

function layoutMatrix(params) {
	const { rings, sectors } = params;
	const matrix = [];
	for (let r = 0; r < rings; r += 1) {
		matrix.push(new Array(sectors(r)).fill(null));
	}
	return matrix;
}

function maskFunctions() {
	return [
		(r,s)=>((r+s)&1)===0,
		(r)=> (r&1)===0,
		(_,s)=> (s%3)===0,
		(r,s)=> ((r+s)%3)===0,
		(r,s)=> (((r/2|0)+(s/3|0))&1)===0,
		(r,s)=> ((r*s)%2 + (r*s)%3)===0,
		(r,s)=> ((((r*s)%2 + (r*s)%3)&1)===0),
		(r,s)=> ((((r+s)%2 + (r*s)%3)&1)===0)
	];
}

function penaltyScore(mat) {
	// Simple N1: consecutive in ring/sector
	let score = 0;
	for (let r = 0; r < mat.length; r += 1) {
		let run = 1;
		for (let s = 1; s < mat[r].length; s += 1) {
			if (mat[r][s] === mat[r][s-1]) { run += 1; if (run >= 5) score += (run - 4); }
			else run = 1;
		}
	}
	return score;
}

function writeBitsToMatrix(bits, params) {
	const mat = layoutMatrix(params);
	let i = 0;
	for (let r = 0; r < params.rings; r += 1) {
		const S = params.sectors(r);
		const forward = (r % 2) === 0;
		if (forward) {
			for (let s = 0; s < S; s += 1) { if (i < bits.length) mat[r][s] = bits[i++]; else mat[r][s] = 0; }
		} else {
			for (let s = S-1; s >= 0; s -= 1) { if (i < bits.length) mat[r][s] = bits[i++]; else mat[r][s] = 0; }
		}
	}
	return mat;
}

function applyBestMask(mat) {
	const masks = maskFunctions();
	let best = { id: 0, score: Infinity, data: mat };
	for (let m = 0; m < masks.length; m += 1) {
		const f = masks[m];
		const mm = mat.map((row, r) => row.map((v, s) => v ^ (f(r,s)?1:0)));
		const sc = penaltyScore(mm);
		if (sc < best.score) best = { id: m, score: sc, data: mm };
	}
	return best;
}

export function encodeSunCode(dataStr, options = {}) {
	const rawBits = encodeDataBytes(dataStr);
	const version = chooseVersion(rawBits.length);
	const params = defaultParams(version);
	const mat = writeBitsToMatrix(rawBits, params);
	const masked = applyBestMask(mat);
	return { params: { ...params, mask: masked.id }, matrix: masked.data };
}

export function renderSunCodeSVG(model, size = 240, options = {}) {
	const { params, matrix } = model;
	const R = params.rings;
	const pad = 6; // quiet halo px
	const w = size, h = size;
	const cx = w/2, cy = h/2;
	const baseWidth = (Math.min(w,h)/2 - pad) / (R + 1); // leave outer quiet ring
	const thickScale = typeof options.thickness === 'number' ? Math.max(0.1, Math.min(1, options.thickness)) : 0.9;
	const ringWidth = baseWidth;
	let svg = `<svg width="${w}" height="${h}" viewBox="0 0 ${w} ${h}" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges"><rect width="100%" height="100%" fill="#fff"/>`;
	// core
	svg += `<circle cx="${cx}" cy="${cy}" r="${ringWidth*thickScale}" fill="#111"/>`;
	for (let r = 0; r < R; r += 1) {
		const S = params.sectors(r);
		const inner = pad + ringWidth * (r + 1);
		const outer = inner + ringWidth * thickScale;
		for (let s = 0; s < S; s += 1) {
			if (!matrix[r][s]) continue;
			const a0 = (s / S) * 2 * Math.PI;
			const a1 = ((s+1) / S) * 2 * Math.PI;
			const path = ringSectorPath(cx, cy, inner, outer, a0, a1);
			svg += `<path d="${path}" fill="#111"/>`;
		}
	}
	svg += `</svg>`;
	return svg;
}

function ringSectorPath(cx, cy, r0, r1, a0, a1) {
	const p0 = polar(cx, cy, r0, a0), p1 = polar(cx, cy, r0, a1);
	const p2 = polar(cx, cy, r1, a1), p3 = polar(cx, cy, r1, a0);
	const large = (a1 - a0) > Math.PI ? 1 : 0;
	return [
		`M ${p0.x} ${p0.y}`,
		`A ${r0} ${r0} 0 ${large} 1 ${p1.x} ${p1.y}`,
		`L ${p2.x} ${p2.y}`,
		`A ${r1} ${r1} 0 ${large} 0 ${p3.x} ${p3.y}`,
		'Z'
	].join(' ');
}

function polar(cx, cy, r, a) { return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) }; }

function _polyfillUtf8Encode(str) {
	// Simple utf8 encoder
	const out = [];
	for (let i = 0; i < str.length; i += 1) {
		let c = str.charCodeAt(i);
		if (c < 0x80) out.push(c);
		else if (c < 0x800) { out.push(0xC0 | (c >> 6), 0x80 | (c & 0x3F)); }
		else if (c >= 0xD800 && c <= 0xDBFF) { // surrogate pair
			const c2 = str.charCodeAt(++i);
			const cp = 0x10000 + (((c & 0x3FF) << 10) | (c2 & 0x3FF));
			out.push(0xF0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3F), 0x80 | ((cp >> 6) & 0x3F), 0x80 | (cp & 0x3F));
		} else { out.push(0xE0 | (c >> 12), 0x80 | ((c >> 6) & 0x3F), 0x80 | (c & 0x3F)); }
	}
	return new Uint8Array(out);
}

// 太阳码结构化解码器 - 按照编码逻辑逆向实现

// 按照生成规则进行逆向解码
export function decodeSunCodeByRules(matrix, params = null) {
	if (!matrix || !Array.isArray(matrix)) {
		console.log('无效的矩阵数据');
		return '';
	}
	
	// 如果没有提供参数，尝试检测
	if (!params) {
		const rings = matrix.length;
		const version = Math.max(1, Math.floor((rings - 6) / 2) + 1);
		params = defaultParams(version);
		console.log('检测到版本:', version, '环数:', rings);
	}
	
	const { rings, sectors } = params;
	
	// 验证矩阵尺寸
	if (matrix.length !== rings) {
		console.log('矩阵环数不匹配:', matrix.length, '期望:', rings);
		return '';
	}
	
	console.log('开始按照生成规则逆向解码...');
	
	// 尝试所有8种掩模
	const masks = maskFunctions();
	let bestResult = '';
	let bestScore = 0;
	let bestMaskId = -1;
	
	for (let maskId = 0; maskId < masks.length; maskId++) {
		try {
			console.log(`尝试掩模 ${maskId}...`);
			
			// 应用掩模（逆向操作）
			const maskFunc = masks[maskId];
			const unmaskedMatrix = matrix.map((row, r) => 
				row.map((bit, s) => bit ^ (maskFunc(r, s) ? 1 : 0))
			);
			
			// 按照写入规则逆向提取比特
			const bits = extractBitsByRules(unmaskedMatrix, params);
			console.log(`掩模 ${maskId} 提取到 ${bits.length} 比特`);
			
			// 解析头部信息
			const header = parseHeaderByRules(bits);
			if (!header) {
				console.log(`掩模 ${maskId} 头部解析失败`);
				continue;
			}
			
			console.log(`掩模 ${maskId} 头部解析成功:`, header);
			
			// 提取数据部分
			const dataBits = bits.slice(header.headerLength, header.headerLength + header.length * 8);
			if (dataBits.length < header.length * 8) {
				console.log(`掩模 ${maskId} 数据长度不足`);
				continue;
			}
			
			// 移除填充
			const cleanBits = removePaddingByRules(dataBits);
			console.log(`掩模 ${maskId} 清理后比特数:`, cleanBits.length);
			
			// 转换为字节
			const bytes = fromBitArray(cleanBits);
			console.log(`掩模 ${maskId} 字节数:`, bytes.length);
			
			// 解码为字符串
			const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
			const text = decoder ? decoder.decode(bytes) : _polyfillUtf8Decode(bytes);
			
			console.log(`掩模 ${maskId} 解码结果:`, text);
			
			// 验证结果
			if (text && text.length > 0 && isValidText(text)) {
				const score = calculateDecodeScore(text, header.length, cleanBits.length);
				console.log(`掩模 ${maskId} 得分:`, score);
				
				if (score > bestScore) {
					bestScore = score;
					bestResult = text;
					bestMaskId = maskId;
					console.log(`掩模 ${maskId} 成为最佳结果`);
				}
			}
		} catch (e) {
			console.log(`掩模 ${maskId} 处理错误:`, e.message);
			continue;
		}
	}
	
	if (bestResult) {
		console.log(`解码成功! 使用掩模 ${bestMaskId}, 得分: ${bestScore}`);
	} else {
		console.log('所有掩模都失败了');
	}
	
	return bestResult;
}

// 按照写入规则逆向提取比特
function extractBitsByRules(matrix, params) {
	const { rings, sectors } = params;
	const bits = [];
	
	console.log('按照写入规则提取比特...');
	
	for (let r = 0; r < rings; r++) {
		const S = sectors(r);
		const forward = (r % 2) === 0;
		
		console.log(`环 ${r}: 扇区数 ${S}, 方向 ${forward ? '正向' : '反向'}`);
		
		if (forward) {
			// 偶数环：正向读取
			for (let s = 0; s < S; s++) {
				bits.push(matrix[r][s] ? 1 : 0);
			}
		} else {
			// 奇数环：反向读取
			for (let s = S - 1; s >= 0; s--) {
				bits.push(matrix[r][s] ? 1 : 0);
			}
		}
	}
	
	console.log(`总共提取 ${bits.length} 比特`);
	return bits;
}

// 按照生成规则解析头部
function parseHeaderByRules(bits) {
	if (bits.length < 20) {
		console.log('比特数不足，无法解析头部');
		return null;
	}
	
	// 检查模式位 (前4位应该是0100)
	const mode = (bits[0] << 3) | (bits[1] << 2) | (bits[2] << 1) | bits[3];
	console.log('模式位:', mode.toString(2).padStart(4, '0'));
	
	if (mode !== 0b0100) {
		console.log('模式位不匹配，期望 0100，实际:', mode.toString(2).padStart(4, '0'));
		return null;
	}
	
	// 解析长度 (16位)
	let len = 0;
	for (let k = 4; k < 20; k++) {
		len = (len << 1) | (bits[k] & 1);
	}
	
	console.log('数据长度:', len);
	
	return { mode, length: len, headerLength: 20 };
}

// 按照生成规则移除填充
function removePaddingByRules(bits) {
	console.log('移除填充，原始比特数:', bits.length);
	
	// 查找填充模式 0xEC (11101100) 和 0x11 (00010001)
	const padPatterns = [
		[1,1,1,0,1,1,0,0], // 0xEC
		[0,0,0,1,0,0,0,1]  // 0x11
	];
	
	let cleanBits = [...bits];
	let removedCount = 0;
	
	// 从末尾开始查找并移除填充
	for (let i = cleanBits.length - 8; i >= 0; i -= 8) {
		const chunk = cleanBits.slice(i, i + 8);
		let isPadding = false;
		
		for (const pattern of padPatterns) {
			if (chunk.every((bit, idx) => bit === pattern[idx])) {
				cleanBits = cleanBits.slice(0, i);
				removedCount += 8;
				isPadding = true;
				console.log(`移除填充块，位置: ${i}`);
				break;
			}
		}
		
		if (!isPadding) break;
	}
	
	console.log(`移除填充完成，移除 ${removedCount} 比特，剩余 ${cleanBits.length} 比特`);
	return cleanBits;
}

// 检测边界基准点
function detectBoundaryMarkers(imageData, width, height) {
	const markers = [];
	const markerSize = 8;
	
	console.log('开始检测边界基准点...');
	
	// 在图像中搜索三个边界基准点
	// 基准点特征：外白内黑中心白的同心圆结构
	for (let y = markerSize; y < height - markerSize; y += 4) {
		for (let x = markerSize; x < width - markerSize; x += 4) {
			if (isBoundaryMarker(imageData, width, height, x, y, markerSize)) {
				const confidence = calculateMarkerConfidence(imageData, width, height, x, y, markerSize);
				markers.push({ x, y, confidence });
				console.log('检测到基准点:', { x, y, confidence });
			}
		}
	}
	
	console.log('检测到基准点总数:', markers.length);
	
	// 按置信度排序，返回前3个
	markers.sort((a, b) => b.confidence - a.confidence);
	return markers.slice(0, 3);
}

// 检查是否为边界基准点 - 简化版本
function isBoundaryMarker(imageData, width, height, x, y, markerSize) {
	// 简化的检测逻辑：检查中心区域是否为暗色，周围区域是否为亮色
	const centerRadius = 3;
	const outerRadius = markerSize;
	
	// 检查中心区域是否为暗色
	let centerDarkCount = 0;
	let centerTotal = 0;
	for (let dy = -centerRadius; dy <= centerRadius; dy++) {
		for (let dx = -centerRadius; dx <= centerRadius; dx++) {
			const px = x + dx;
			const py = y + dy;
			if (px < 0 || px >= width || py < 0 || py >= height) continue;
			
			const idx = (py * width + px) * 4;
			const r = imageData[idx];
			const g = imageData[idx + 1];
			const b = imageData[idx + 2];
			const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
			
			centerTotal++;
			if (lum < 128) centerDarkCount++;
		}
	}
	
	// 检查周围区域是否为亮色
	let outerBrightCount = 0;
	let outerTotal = 0;
	for (let angle = 0; angle < 16; angle++) {
		const a = (angle / 16) * 2 * Math.PI;
		const px = Math.round(x + outerRadius * Math.cos(a));
		const py = Math.round(y + outerRadius * Math.sin(a));
		if (px < 0 || px >= width || py < 0 || py >= height) continue;
		
		const idx = (py * width + px) * 4;
		const r = imageData[idx];
		const g = imageData[idx + 1];
		const b = imageData[idx + 2];
		const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
		
		outerTotal++;
		if (lum > 128) outerBrightCount++;
	}
	
	// 中心区域至少70%为暗色，周围区域至少60%为亮色
	const centerDarkRatio = centerTotal > 0 ? centerDarkCount / centerTotal : 0;
	const outerBrightRatio = outerTotal > 0 ? outerBrightCount / outerTotal : 0;
	
	return centerDarkRatio >= 0.7 && outerBrightRatio >= 0.6;
}

// 计算基准点置信度
function calculateMarkerConfidence(imageData, width, height, x, y, markerSize) {
	let confidence = 0;
	const outerRadius = markerSize;
	const innerRadius = markerSize - 3;
	const centerRadius = markerSize - 6;
	
	// 检查同心圆结构的完整性
	for (let r = 1; r <= outerRadius; r++) {
		for (let angle = 0; angle < 16; angle++) {
			const a = (angle / 16) * 2 * Math.PI;
			const px = Math.round(x + r * Math.cos(a));
			const py = Math.round(y + r * Math.sin(a));
			if (px < 0 || px >= width || py < 0 || py >= height) continue;
			
			const idx = (py * width + px) * 4;
			const r_val = imageData[idx];
			const g_val = imageData[idx + 1];
			const b_val = imageData[idx + 2];
			const lum = 0.2126 * r_val + 0.7152 * g_val + 0.0722 * b_val;
			
			// 根据位置判断期望的亮度
			let expectedLum;
			if (r <= centerRadius) {
				expectedLum = 255; // 中心白色
			} else if (r <= innerRadius) {
				expectedLum = 0; // 内圆黑色
			} else {
				expectedLum = 255; // 外圆白色
			}
			
			// 计算匹配度
			const match = 1 - Math.abs(lum - expectedLum) / 255;
			confidence += match;
		}
	}
	
	return confidence;
}

// 从边界基准点计算太阳码中心
function calculateCenterFromMarkers(markers) {
	if (markers.length < 3) return null;
	
	// 使用三个基准点计算中心位置
	// 假设基准点按120度间隔分布
	const p1 = markers[0];
	const p2 = markers[1];
	const p3 = markers[2];
	
	// 计算重心
	const cx = (p1.x + p2.x + p3.x) / 3;
	const cy = (p1.y + p2.y + p3.y) / 3;
	
	// 计算平均距离
	const dist1 = Math.sqrt((p1.x - cx) ** 2 + (p1.y - cy) ** 2);
	const dist2 = Math.sqrt((p2.x - cx) ** 2 + (p2.y - cy) ** 2);
	const dist3 = Math.sqrt((p3.x - cx) ** 2 + (p3.y - cy) ** 2);
	const avgDist = (dist1 + dist2 + dist3) / 3;
	
	return { x: cx, y: cy, radius: avgDist, confidence: calculateCenterConfidence(markers, cx, cy) };
}

// 计算中心位置置信度
function calculateCenterConfidence(markers, cx, cy) {
	if (markers.length < 3) return 0;
	
	let confidence = 0;
	const expectedAngle = 2 * Math.PI / 3; // 120度
	
	for (let i = 0; i < markers.length; i++) {
		const marker = markers[i];
		const dx = marker.x - cx;
		const dy = marker.y - cy;
		const dist = Math.sqrt(dx * dx + dy * dy);
		const angle = Math.atan2(dy, dx);
		
		// 距离一致性
		const expectedDist = (markers[0].x - cx) ** 2 + (markers[0].y - cy) ** 2;
		const actualDist = dx * dx + dy * dy;
		const distMatch = 1 - Math.abs(actualDist - expectedDist) / expectedDist;
		confidence += Math.max(0, distMatch);
		
		// 角度一致性
		const expectedAngle_i = i * expectedAngle;
		const angleDiff = Math.abs(angle - expectedAngle_i);
		const angleMatch = 1 - Math.min(angleDiff, 2 * Math.PI - angleDiff) / Math.PI;
		confidence += Math.max(0, angleMatch);
	}
	
	return confidence / (markers.length * 2);
}

// 从矩阵中按编码顺序提取比特
function extractBitsFromMatrix(matrix, params) {
	const { rings, sectors } = params;
	const bits = [];
	
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const forward = (r % 2) === 0;
		if (forward) {
			for (let s = 0; s < S; s += 1) {
				bits.push(matrix[r][s] ? 1 : 0);
			}
		} else {
			for (let s = S - 1; s >= 0; s -= 1) {
				bits.push(matrix[r][s] ? 1 : 0);
			}
		}
	}
	return bits;
}

// 解析头部信息
function parseHeader(bits) {
	if (bits.length < 20) return null;
	
	// 检查模式位 (前4位应该是0100)
	const mode = (bits[0] << 3) | (bits[1] << 2) | (bits[2] << 1) | bits[3];
	if (mode !== 0b0100) return null;
	
	// 解析长度 (16位)
	let len = 0;
	for (let k = 4; k < 20; k += 1) {
		len = (len << 1) | (bits[k] & 1);
	}
	
	return { mode, length: len, headerLength: 20 };
}

// 移除填充字节
function removePadding(bits) {
	// 查找填充模式 0xEC (11101100) 和 0x11 (00010001)
	const padPatterns = [
		[1,1,1,0,1,1,0,0], // 0xEC
		[0,0,0,1,0,0,0,1]  // 0x11
	];
	
	let cleanBits = [...bits];
	
	// 从末尾开始查找并移除填充
	for (let i = cleanBits.length - 8; i >= 0; i -= 8) {
		const chunk = cleanBits.slice(i, i + 8);
		let isPadding = false;
		
		for (const pattern of padPatterns) {
			if (chunk.every((bit, idx) => bit === pattern[idx])) {
				cleanBits = cleanBits.slice(0, i);
				isPadding = true;
				break;
			}
		}
		
		if (!isPadding) break;
	}
	
	return cleanBits;
}

// 太阳码结构化解码
export function decodeSunCodeStructured(matrix, params = null) {
	if (!matrix || !Array.isArray(matrix)) return '';
	
	// 如果没有提供参数，尝试检测
	if (!params) {
		const rings = matrix.length;
		const version = Math.max(1, Math.floor((rings - 6) / 2) + 1);
		params = defaultParams(version);
	}
	
	const { rings, sectors } = params;
	
	// 验证矩阵尺寸
	if (matrix.length !== rings) return '';
	
	// 尝试所有掩模
	const masks = maskFunctions();
	let bestResult = '';
	let bestScore = 0;
	
	for (let maskId = 0; maskId < masks.length; maskId += 1) {
		try {
			// 应用掩模
			const maskFunc = masks[maskId];
			const unmaskedMatrix = matrix.map((row, r) => 
				row.map((bit, s) => bit ^ (maskFunc(r, s) ? 1 : 0))
			);
			
			// 提取比特
			const bits = extractBitsFromMatrix(unmaskedMatrix, params);
			
			// 解析头部
			const header = parseHeader(bits);
			if (!header) continue;
			
			// 提取数据部分
			const dataBits = bits.slice(header.headerLength, header.headerLength + header.length * 8);
			if (dataBits.length < header.length * 8) continue;
			
			// 移除填充
			const cleanBits = removePadding(dataBits);
			
			// 转换为字节
			const bytes = fromBitArray(cleanBits);
			
			// 解码为字符串
			const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
			const text = decoder ? decoder.decode(bytes) : _polyfillUtf8Decode(bytes);
			
			// 验证结果
			if (text && text.length > 0 && isValidText(text)) {
				// 计算置信度分数
				const score = calculateDecodeScore(text, header.length, cleanBits.length);
				if (score > bestScore) {
					bestScore = score;
					bestResult = text;
				}
			}
		} catch (e) {
			// 继续尝试下一个掩模
			continue;
		}
	}
	
	return bestResult;
}

// 计算解码置信度分数
function calculateDecodeScore(text, expectedLength, actualBits) {
	let score = 0;
	
	// 长度匹配度
	const lengthRatio = Math.min(text.length / expectedLength, 1);
	score += lengthRatio * 40;
	
	// 文本有效性
	if (isValidText(text)) score += 30;
	
	// 比特利用率
	const bitRatio = actualBits / (expectedLength * 8);
	if (bitRatio > 0.8 && bitRatio < 1.2) score += 20;
	
	// 文本长度合理性
	if (text.length > 0 && text.length < 1000) score += 10;
	
	return score;
}

// 文本有效性检查
function isValidText(text) {
	if (!text || text.length === 0) return false;
	
	// 检查是否包含可打印字符
	const printableRegex = /^[\x20-\x7E\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]*$/;
	return printableRegex.test(text);
}

// 图像预处理 - 简化版本
function preprocessImage(imageData, width, height) {
	// 简化预处理：只进行基本的对比度增强
	const processedData = new Uint8ClampedArray(imageData);
	
	// 计算图像的平均亮度
	let totalLum = 0;
	let pixelCount = 0;
	for (let i = 0; i < imageData.length; i += 4) {
		const r = imageData[i];
		const g = imageData[i + 1];
		const b = imageData[i + 2];
		const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
		totalLum += lum;
		pixelCount++;
	}
	const avgLum = totalLum / pixelCount;
	
	// 根据平均亮度调整对比度
	const contrastFactor = avgLum < 100 ? 1.5 : (avgLum > 200 ? 0.8 : 1.0);
	
	for (let i = 0; i < imageData.length; i += 4) {
		const r = Math.max(0, Math.min(255, (imageData[i] - avgLum) * contrastFactor + avgLum));
		const g = Math.max(0, Math.min(255, (imageData[i + 1] - avgLum) * contrastFactor + avgLum));
		const b = Math.max(0, Math.min(255, (imageData[i + 2] - avgLum) * contrastFactor + avgLum));
		
		processedData[i] = r;
		processedData[i + 1] = g;
		processedData[i + 2] = b;
		processedData[i + 3] = imageData[i + 3]; // 保持alpha通道
	}
	
	return processedData;
}

// 多尺度检测 - 适应不同大小的太阳码
function detectSunCodeMultiScale(imageData, width, height) {
	const scales = [0.6, 0.8, 1.0, 1.2, 1.4];
	const results = [];
	
	for (const scale of scales) {
		const scaledWidth = Math.round(width * scale);
		const scaledHeight = Math.round(height * scale);
		
		// 缩放图像
		const scaledData = scaleImage(imageData, width, height, scaledWidth, scaledHeight);
		
		// 检测边界基准点
		const markers = detectBoundaryMarkers(scaledData, scaledWidth, scaledHeight);
		if (markers.length >= 3) {
			const detectedCenter = calculateCenterFromMarkers(markers);
			if (detectedCenter && detectedCenter.confidence > 0.3) {
				// 将坐标转换回原始尺寸
				const originalCenter = {
					x: detectedCenter.x / scale,
					y: detectedCenter.y / scale,
					radius: detectedCenter.radius / scale,
					confidence: detectedCenter.confidence,
					scale: scale
				};
				results.push(originalCenter);
			}
		}
	}
	
	// 返回置信度最高的结果
	results.sort((a, b) => b.confidence - a.confidence);
	return results[0] || null;
}

// 图像缩放
function scaleImage(imageData, srcWidth, srcHeight, dstWidth, dstHeight) {
	const scaledData = new Uint8ClampedArray(dstWidth * dstHeight * 4);
	const xRatio = srcWidth / dstWidth;
	const yRatio = srcHeight / dstHeight;
	
	for (let y = 0; y < dstHeight; y++) {
		for (let x = 0; x < dstWidth; x++) {
			const srcX = Math.floor(x * xRatio);
			const srcY = Math.floor(y * yRatio);
			const srcIdx = (srcY * srcWidth + srcX) * 4;
			const dstIdx = (y * dstWidth + x) * 4;
			
			scaledData[dstIdx] = imageData[srcIdx];
			scaledData[dstIdx + 1] = imageData[srcIdx + 1];
			scaledData[dstIdx + 2] = imageData[srcIdx + 2];
			scaledData[dstIdx + 3] = imageData[srcIdx + 3];
		}
	}
	
	return scaledData;
}

// 从图像数据解码太阳码
export async function decodeSunCodeFromImageData(imageData, width, height, center = null) {
	// 图像预处理
	const processedImageData = preprocessImage(imageData, width, height);
	
	// 使用传入或默认中心
	const cx = center ? center.x : width / 2;
	const cy = center ? center.y : height / 2;
	const maxR = Math.min(width, height) / 2 * 0.8;
	
	// 基础配置顺序
	const testConfigs = getOptimizedConfigs(null, null);
	
	// 基础中心搜索
	const centers = getOptimizedCenters(null, cx, cy, maxR);
	
	let bestResult = '';
	let bestScore = 0;
	let bestConfig = null;
	let bestCenter = null;
	
	// 并行解码尝试
	const decodePromises = [];
	
	for (const config of testConfigs) {
		for (const center of centers) {
			decodePromises.push(
				decodeWithConfig(processedImageData, width, height, center, maxR, config, null)
			);
		}
	}
	
	try {
		const results = await Promise.allSettled(decodePromises);
		
		for (let i = 0; i < results.length; i++) {
			const result = results[i];
			if (result.status === 'fulfilled' && result.value && result.value.text) {
				const { text, score, config, center } = result.value;
				if (score > bestScore) {
					bestScore = score;
					bestResult = text;
					bestConfig = config;
					bestCenter = center;
				}
			}
		}
	} catch (e) {
		console.error('并行解码错误:', e);
	}
	
	// 如果并行解码失败，尝试串行解码
	if (!bestResult) {
		console.log('并行解码失败，尝试串行解码...');
		for (const config of testConfigs) {
			for (const center of centers) {
				try {
					const result = await decodeWithConfig(processedImageData, width, height, center, maxR, config, null);
					if (result && result.text && result.score > bestScore) {
						bestScore = result.score;
						bestResult = result.text;
						bestConfig = result.config;
						bestCenter = result.center;
					}
				} catch (e) {
					continue;
				}
			}
		}
	}
	
	// 如果仍然失败，尝试传统网格搜索
	if (!bestResult) {
		console.log('串行解码失败，尝试传统网格搜索...');
		const traditionalCenters = generateTraditionalCenters(width, height);
		const traditionalConfigs = [
		{ version: 1, rings: 6, sectors: (r) => 32 + 4 * r },
		{ version: 2, rings: 8, sectors: (r) => 36 + 4 * r },
			{ version: 3, rings: 10, sectors: (r) => 40 + 4 * r }
		];
		
		for (const config of traditionalConfigs) {
			for (const center of traditionalCenters) {
				try {
					const result = await decodeWithConfig(processedImageData, width, height, center, maxR, config, null);
					if (result && result.text && result.score > bestScore) {
						bestScore = result.score;
						bestResult = result.text;
						bestConfig = result.config;
						bestCenter = result.center;
					}
				} catch (e) {
					continue;
				}
			}
		}
	}
	
	if (bestResult) {
		console.log('解码成功:', { text: bestResult, score: bestScore, config: bestConfig, center: bestCenter });
	}
	
	return bestResult;
}

// 生成传统网格搜索中心点
function generateTraditionalCenters(width, height) {
	const centers = [];
	const step = 10;
	
	for (let y = 50; y < height - 50; y += step) {
		for (let x = 50; x < width - 50; x += step) {
			centers.push({ x, y });
		}
	}
	
	return centers;
}

// 智能配置选择 - 根据检测结果优化配置顺序
function getOptimizedConfigs(detectedCenter, multiScaleResult) {
	const baseConfigs = [
		{ version: 1, rings: 6, sectors: (r) => 32 + 4 * r, priority: 1 },
		{ version: 2, rings: 8, sectors: (r) => 36 + 4 * r, priority: 2 },
		{ version: 3, rings: 10, sectors: (r) => 40 + 4 * r, priority: 3 },
		{ version: 4, rings: 12, sectors: (r) => 44 + 4 * r, priority: 4 },
		{ version: 5, rings: 14, sectors: (r) => 48 + 4 * r, priority: 5 }
	];
	
	// 如果有检测结果，优先使用相关配置
	if (detectedCenter && detectedCenter.confidence > 0.5) {
		// 根据检测到的半径估算版本
		const estimatedVersion = Math.min(5, Math.max(1, Math.round(detectedCenter.radius / 20)));
		baseConfigs.forEach(config => {
			if (config.version === estimatedVersion) {
				config.priority = 0; // 最高优先级
			}
		});
	}
	
	// 如果有多尺度检测结果，优先使用相关配置
	if (multiScaleResult && multiScaleResult.scale) {
		const scale = multiScaleResult.scale;
		if (scale < 0.8) {
			// 小图像，优先使用小版本
			baseConfigs.forEach(config => {
				if (config.version <= 2) config.priority -= 2;
			});
		} else if (scale > 1.2) {
			// 大图像，优先使用大版本
			baseConfigs.forEach(config => {
				if (config.version >= 4) config.priority -= 2;
			});
		}
	}
	
	// 按优先级排序
	return baseConfigs.sort((a, b) => a.priority - b.priority);
}

// 智能中心位置搜索
function getOptimizedCenters(detectedCenter, cx, cy, maxR) {
	if (detectedCenter && detectedCenter.confidence > 0.5) {
		// 高置信度检测结果，使用精确搜索
		return [
			{ x: cx, y: cy },
			{ x: cx + 1, y: cy },
			{ x: cx - 1, y: cy },
			{ x: cx, y: cy + 1 },
			{ x: cx, y: cy - 1 },
			{ x: cx + 2, y: cy },
			{ x: cx - 2, y: cy },
			{ x: cx, y: cy + 2 },
			{ x: cx, y: cy - 2 }
		];
	} else if (detectedCenter && detectedCenter.confidence > 0.3) {
		// 中等置信度，使用中等搜索范围
		return [
		{ x: cx, y: cy },
		{ x: cx + 2, y: cy },
		{ x: cx - 2, y: cy },
		{ x: cx, y: cy + 2 },
		{ x: cx, y: cy - 2 },
		{ x: cx + 4, y: cy },
		{ x: cx - 4, y: cy },
		{ x: cx, y: cy + 4 },
		{ x: cx, y: cy - 4 }
	];
	} else {
		// 低置信度或无检测结果，使用广泛搜索
		return [
			{ x: cx, y: cy },
			{ x: cx + 2, y: cy },
			{ x: cx - 2, y: cy },
			{ x: cx, y: cy + 2 },
			{ x: cx, y: cy - 2 },
			{ x: cx + 4, y: cy },
			{ x: cx - 4, y: cy },
			{ x: cx, y: cy + 4 },
			{ x: cx, y: cy - 4 },
			{ x: cx + 6, y: cy },
			{ x: cx - 6, y: cy },
			{ x: cx, y: cy + 6 },
			{ x: cx, y: cy - 6 }
		];
	}
}

// 单个配置解码
async function decodeWithConfig(imageData, width, height, center, maxR, config, detectedCenter) {
			try {
				const matrix = await sampleMatrixFromImage(imageData, width, height, center.x, center.y, maxR, config);
		
		// 首先尝试规则解码器
		let result = decodeSunCodeByRules(matrix, config);
		
		// 如果规则解码失败，尝试结构化解码器
		if (!result) {
			console.log('规则解码失败，尝试结构化解码器...');
			result = decodeSunCodeStructured(matrix, config);
		}
				
				if (result && result.length > 0) {
					const score = calculateDecodeScore(result, result.length, result.length * 8);
			// 如果使用了边界基准检测，给予额外加分
			if (detectedCenter && detectedCenter.confidence > 0.5) {
				score += 20;
			}
			// 如果中心位置接近检测位置，给予额外加分
			if (detectedCenter) {
				const distance = Math.sqrt((center.x - detectedCenter.x) ** 2 + (center.y - detectedCenter.y) ** 2);
				if (distance < 5) {
					score += 10;
				}
			}
			
			return {
				text: result,
				score: score,
				config: config,
				center: center
			};
		}
		return null;
			} catch (e) {
		console.log('解码配置失败:', e.message);
		return null;
	}
}

// 自适应阈值计算
function calculateAdaptiveThreshold(luminances) {
	if (luminances.length === 0) return 128;
	
	// 使用Otsu算法计算最佳阈值
	const histogram = new Array(256).fill(0);
	for (const lum of luminances) {
		histogram[Math.round(lum)]++;
	}
	
	const total = luminances.length;
	let sum = 0;
	for (let i = 0; i < 256; i++) {
		sum += i * histogram[i];
	}
	
	let sumB = 0;
	let wB = 0;
	let wF = 0;
	let varMax = -1;
	let threshold = 128;
	
	for (let t = 0; t < 256; t++) {
		wB += histogram[t];
		if (wB === 0) continue;
		
		wF = total - wB;
		if (wF === 0) break;
		
		sumB += t * histogram[t];
		const mB = sumB / wB;
		const mF = (sum - sumB) / wF;
		const between = wB * wF * (mB - mF) * (mB - mF);
		
		if (between > varMax) {
			varMax = between;
			threshold = t;
		}
	}
	
	return threshold;
}

// 优化的采样策略 - 减少噪声影响
function sampleWithNoiseReduction(imageData, width, height, cx, cy, radius, angle, sampleRadius = 2) {
	let totalLum = 0;
	let sampleCount = 0;
	const luminances = [];
	
	// 在采样点周围进行高斯采样
	for (let dy = -sampleRadius; dy <= sampleRadius; dy++) {
		for (let dx = -sampleRadius; dx <= sampleRadius; dx++) {
			const distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > sampleRadius) continue;
			
			const x = Math.round(cx + radius * Math.cos(angle) + dx);
			const y = Math.round(cy + radius * Math.sin(angle) + dy);
			
			if (x < 0 || x >= width || y < 0 || y >= height) continue;
			
			const idx = (y * width + x) * 4;
			const r = imageData[idx];
			const g = imageData[idx + 1];
			const b = imageData[idx + 2];
			const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
			
			// 高斯权重
			const weight = Math.exp(-(dx * dx + dy * dy) / (2 * sampleRadius * sampleRadius));
			totalLum += lum * weight;
			sampleCount += weight;
			luminances.push(lum);
		}
	}
	
	const avgLum = sampleCount > 0 ? totalLum / sampleCount : 128;
	return { avgLum, luminances };
}

// 从图像数据采样矩阵
async function sampleMatrixFromImage(imageData, width, height, cx, cy, maxR, config) {
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const matrix = [];
	
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const radius = inner + ringWidth * 0.5;
		const ringBits = [];
		const ringLuminances = [];
		
		// 第一遍：收集所有扇区的亮度值
		for (let s = 0; s < S; s += 1) {
			const angle = (s + 0.5) / S * 2 * Math.PI;
			const { avgLum, luminances } = sampleWithNoiseReduction(imageData, width, height, cx, cy, radius, angle);
			ringLuminances.push(...luminances);
		}
		
		// 计算自适应阈值
		const threshold = calculateAdaptiveThreshold(ringLuminances);
		
		// 第二遍：使用自适应阈值进行二值化
		for (let s = 0; s < S; s += 1) {
			const angle = (s + 0.5) / S * 2 * Math.PI;
			const { avgLum } = sampleWithNoiseReduction(imageData, width, height, cx, cy, radius, angle);
			ringBits.push(avgLum < threshold ? 1 : 0);
		}
		
		matrix.push(ringBits);
	}
	
	return matrix;
}

export default { encodeSunCode, renderSunCodeSVG, decodeSunCodeStructured, decodeSunCodeFromImageData, decodeSunCodeByRules };
export async function decodeSunCodeWithReader(reader, width = 300, height = 300, center = null) {
	const w = width, h = height;
	const baseCx = center ? center.x : w/2, baseCy = center ? center.y : h/2;
	const maxR = Math.min(w,h)/2 * 0.8;
	const testConfigs = [
		{ rings: 6, sectors: (r) => 32 + 4 * r },
		{ rings: 8, sectors: (r) => 36 + 4 * r },
		{ rings: 4, sectors: (r) => 28 + 4 * r }
	];
	const centers = [
		{ x: baseCx, y: baseCy },
		{ x: baseCx + 4, y: baseCy },
		{ x: baseCx - 4, y: baseCy },
		{ x: baseCx, y: baseCy + 4 },
		{ x: baseCx, y: baseCy - 4 },
	];
	for (const c of centers) {
		for (const config of testConfigs) {
			try {
				const result = await tryDecodeWithReader(reader, w, h, c.x, c.y, maxR, config);
				if (result && result.trim()) return result;
			} catch (e) {
				// try next
			}
		}
	}
	return '';
}

async function tryDecodeWithReader(reader, width, height, cx, cy, maxR, config) {
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const bits = [];
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const radius = inner + ringWidth * 0.5;
		let w = width||300, h = height||300;

		// 第一遍：求该环每一扇区的平均亮度
		const lumList = new Array(S);
		for (let s = 0; s < S; s += 1) {
			const a = (s + 0.5) / S * 2 * Math.PI;
			const x = Math.round(cx + radius * Math.cos(a));
			const y = Math.round(cy + radius * Math.sin(a));
			let totalLum = 0, sampleCount = 0;
			for (let dx = -1; dx <= 1; dx += 1) {
				for (let dy = -1; dy <= 1; dy += 1) {
					const sx = Math.max(0, Math.min(w-1, x + dx));
					const sy = Math.max(0, Math.min(h-1, y + dy));
					const val = await reader(sx, sy);
					totalLum += val;
					sampleCount += 1;
				}
			}
			lumList[s] = totalLum / sampleCount;
		}
		// 自适应阈值：中位数（抗极值），退化到均值
		const sorted = lumList.slice().sort((a,b)=>a-b);
		const median = sorted[Math.floor(sorted.length/2)];
		const mean = sorted.reduce((a,b)=>a+b,0)/sorted.length;
		const thr = isFinite(median) ? median : mean;

		// 第二遍：按环阈值判定 bit
		for (let s = 0; s < S; s += 1) {
			bits.push(lumList[s] < thr ? 1 : 0);
		}
	}
	const bytes = fromBitArray(bits);
	const dec = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
	for (let start = 0; start < bytes.length - 4; start += 1) {
	 for (let len = 1; len <= Math.min(50, bytes.length - start); len += 1) {
		try {
			const slice = bytes.slice(start, start + len);
			const text = dec ? dec.decode(slice) : _polyfillUtf8Decode(slice);
			if (text && text.trim() && isValidText(text)) {
				return text.trim();
			}
		} catch (e) {}
	 }
	}
	throw new Error('No valid text found');
}
// --- Decoder (simplified MVP) ---
export async function decodeSunCodeFromCanvas(canvasCtx,width,height, center = null) {
	// Assumes the SunCode is roughly centered and fills most of the canvas
	const ctx = canvasCtx;
	const w = width||300, h = height||300;
	const cx = center ? center.x : w/2, cy = center ? center.y : h/2;
	const maxR = Math.min(w,h)/2 * 0.8; // Reduce to avoid edge sampling
	// Try different ring counts and sizes
	const testConfigs = [
		{ rings: 6, sectors: (r) => 32 + 4 * r }, // v=1
		{ rings: 8, sectors: (r) => 36 + 4 * r }, // v=2
		{ rings: 4, sectors: (r) => 28 + 4 * r }  // smaller
	];
	
	for (const config of testConfigs) {
		try {
			const result = await tryDecodeWithConfig(canvasCtx,width,height, cx, cy, maxR, config);
			if (result && result.trim()) return result;
		} catch (e) {
			console.log('Config failed:', config, e.message);
		}
	}
	
	return ''; // No valid decode found
}

async function tryDecodeWithConfig(canvasCtx, cx, cy, maxR, config) {
	const ctx = canvasCtx;
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const bits = [];
	
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const radius = inner + ringWidth * 0.5;
		
		let w = width||300, h = height||300;
		for (let s = 0; s < S; s += 1) {
			const a = (s + 0.5) / S * 2 * Math.PI;
			const x = Math.round(cx + radius * Math.cos(a));
			const y = Math.round(cy + radius * Math.sin(a));
			
			// Sample multiple points for better accuracy
			let totalLum = 0, sampleCount = 0;
			for (let dx = -1; dx <= 1; dx += 1) {
				for (let dy = -1; dy <= 1; dy += 1) {
					const sx = Math.max(0, Math.min(w-1, x + dx));
					const sy = Math.max(0, Math.min(h-1, y + dy));
					const p = ctx.getImageData(sx, sy, 1, 1).data;
					const lum = 0.2126*p[0] + 0.7152*p[1] + 0.0722*p[2];
					totalLum += lum;
					sampleCount += 1;
				}
			}
			const avgLum = totalLum / sampleCount;
			bits.push(avgLum < 128 ? 1 : 0);
		}
	}
	
	// Try to find and decode data
	const bytes = fromBitArray(bits);
	const dec = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
	
	// Look for valid UTF-8 sequences
	for (let start = 0; start < bytes.length - 4; start += 1) {
		for (let len = 1; len <= Math.min(50, bytes.length - start); len += 1) {
			try {
				const slice = bytes.slice(start, start + len);
				const text = dec ? dec.decode(slice) : _polyfillUtf8Decode(slice);
				if (text && text.trim() && isValidText(text)) {
					return text.trim();
				}
			} catch (e) {
				// Invalid UTF-8, continue
			}
		}
	}
	
	throw new Error('No valid text found');
}

function findBits(hay, needle) {
	for (let i = 0; i <= hay.length - needle.length; i += 1) {
		let ok = true; for (let k = 0; k < needle.length; k += 1) if (hay[i+k] !== needle[k]) { ok = false; break; }
		if (ok) return i;
	}
	return -1;
}

function _polyfillUtf8Decode(uint8) {
	let out = '';
	for (let i = 0; i < uint8.length;) {
		const b0 = uint8[i++];
		if ((b0 & 0x80) === 0) out += String.fromCharCode(b0);
		else if ((b0 & 0xE0) === 0xC0) { const b1 = uint8[i++]; out += String.fromCharCode(((b0&0x1F)<<6)|(b1&0x3F)); }
		else if ((b0 & 0xF0) === 0xE0) { const b1 = uint8[i++], b2 = uint8[i++]; out += String.fromCharCode(((b0&0x0F)<<12)|((b1&0x3F)<<6)|(b2&0x3F)); }
		else { const b1=uint8[i++],b2=uint8[i++],b3=uint8[i++]; const cp=((b0&0x07)<<18)|((b1&0x3F)<<12)|((b2&0x3F)<<6)|(b3&0x3F); const h=((cp-0x10000)>>10)+0xD800; const l=((cp-0x10000)&0x3FF)+0xDC00; out += String.fromCharCode(h,l); }
	}
	return out;
}
// 严格镜像编码的解码（基于 reader 采样）
export async function decodeSunCodeStrictWithReader(reader, width = 300, height = 300, center = null) {
	const w = width, h = height;
	const baseCx = center ? center.x : w/2, baseCy = center ? center.y : h/2;
	const maxR = Math.min(w,h)/2 * 0.8;

	const testConfigs = [
		{ rings: 6, sectors: (r) => 32 + 4 * r }, // v=1
		{ rings: 8, sectors: (r) => 36 + 4 * r }, // v=2
		{ rings: 4, sectors: (r) => 28 + 4 * r }  // small
	];
	const centers = [
		{ x: baseCx, y: baseCy },
		{ x: baseCx + 4, y: baseCy },
		{ x: baseCx - 4, y: baseCy },
		{ x: baseCx, y: baseCy + 4 },
		{ x: baseCx, y: baseCy - 4 },
	];

	for (const c of centers) {
		for (const cfg of testConfigs) {
			try {
				const matrix = await _sampleMatrixWithReader(reader, w, h, c.x, c.y, maxR, cfg);
				const text = _strictDecodeFromMatrix(matrix);
				if (text) return text;
			} catch (e) {}
		}
	}
	return '';
}

function _readBitsInOrder(mat) {
	// 按编码写入顺序读取 bits
	const bits = [];
	for (let r = 0; r < mat.length; r += 1) {
		const row = mat[r];
		const forward = (r % 2) === 0;
		if (forward) {
			for (let s = 0; s < row.length; s += 1) bits.push(row[s] ? 1 : 0);
		} else {
			for (let s = row.length - 1; s >= 0; s -= 1) bits.push(row[s] ? 1 : 0);
		}
	}
	return bits;
}

function _unmaskMatrix(mat, maskId) {
	const masks = maskFunctions();
	const f = masks[maskId];
	return mat.map((row, r) => row.map((v, s) => (v ^ (f(r,s) ? 1 : 0)) & 1));
}

function _parseHeaderAndPayload(bits) {
	// 头部：4bit模式 + 16bit长度
	if (bits.length < 20) return null;
	const mode = (bits[0]<<3)|(bits[1]<<2)|(bits[2]<<1)|bits[3];
	if (mode !== 0b0100) return null;

	let len = 0;
	for (let k = 0; k < 16; k += 1) len = (len<<1) | (bits[4+k] & 1);
	const payloadStart = 20;
	const payloadBits = bits.slice(payloadStart, payloadStart + len*8);
	if (payloadBits.length < len*8) return null;

	const bytes = fromBitArray(payloadBits);
	const dec = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
	try {
		const text = dec ? dec.decode(bytes) : _polyfillUtf8Decode(bytes);
		return text;
	} catch (e) {
		return null;
	}
}

function _strictDecodeFromMatrix(sampledMatrix) {
	// 尝试 8 个掩码的反掩码
	for (let m = 0; m < 8; m += 1) {
		const unmasked = _unmaskMatrix(sampledMatrix, m);
		const bits = _readBitsInOrder(unmasked);
		const text = _parseHeaderAndPayload(bits);
		if (text && isValidText(text)) return text.trim();
	}
	return null;
}

async function _sampleMatrixWithReader(reader, width, height, cx, cy, maxR, config) {
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const mat = [];
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const radius = inner + ringWidth * 0.5;

		// 先整圈采样亮度，计算环阈值
		const lumList = new Array(S);
		for (let s = 0; s < S; s += 1) {
			const a = (s + 0.5) / S * 2 * Math.PI;
			const x = Math.round(cx + radius * Math.cos(a));
			const y = Math.round(cy + radius * Math.sin(a));
			let total = 0, cnt = 0;
			for (let dx = -1; dx <= 1; dx += 1) {
				for (let dy = -1; dy <= 1; dy += 1) {
					const sx = Math.max(0, Math.min(width-1, x + dx));
					const sy = Math.max(0, Math.min(height-1, y + dy));
					total += await reader(sx, sy);
					cnt += 1;
				}
			}
			lumList[s] = total / cnt;
		}
		const sorted = lumList.slice().sort((a,b)=>a-b);
		const median = sorted[Math.floor(sorted.length/2)];
		const mean = sorted.reduce((a,b)=>a+b,0)/sorted.length;
		const thr = isFinite(median) ? median : mean;

		const row = new Array(S);
		for (let s = 0; s < S; s += 1) row[s] = (lumList[s] < thr ? 1 : 0);
		mat.push(row);
	}
	return mat;
}
function linearizeBitsFromMatrix(mat, sectors) {
	// 按编码时的顺序：ring 递增；偶数环正序，奇数环逆序
	const bits = [];
	for (let r = 0; r < mat.length; r += 1) {
		const S = sectors(r);
		if ((r % 2) === 0) {
			for (let s = 0; s < S; s += 1) bits.push(mat[r][s] ? 1 : 0);
		} else {
			for (let s = S - 1; s >= 0; s -= 1) bits.push(mat[r][s] ? 1 : 0);
		}
	}
	return bits;
}

function unmaskMatrix(mat, maskId) {
	const masks = maskFunctions();
	const f = masks[maskId];
	return mat.map((row, r) => row.map((v, s) => (v ^ (f(r, s) ? 1 : 0)) & 1));
}

function parseHeaderAndPayload(bits) {
	// 4bit 模式 + 16bit 长度 + payload(len bytes)
	if (bits.length < 4 + 16) return null;
	const mode = (bits[0]<<3)|(bits[1]<<2)|(bits[2]<<1)|bits[3];
	if (mode !== 0b0100) return null;
	let len = 0;
	for (let k = 0; k < 16; k += 1) len = (len << 1) | (bits[4 + k] & 1);
	const start = 4 + 16;
	const need = len * 8;
	if (bits.length < start + need) return null;
	const payload = bits.slice(start, start + need);
	return { len, payload };
}

function bitsToBytes(bits) {
	const bytes = [];
	for (let i = 0; i < bits.length; i += 8) {
		let v = 0;
		for (let k = 0; k < 8; k += 1) v = (v << 1) | (bits[i + k] & 1);
		bytes.push(v);
	}
	return new Uint8Array(bytes);
}

async function sampleRingMatrixWithReader(reader, width, height, cx, cy, maxR, config) {
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const mat = [];
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const radius = inner + ringWidth * 0.5;
		const lumList = new Array(S);
		for (let s = 0; s < S; s += 1) {
			const a = (s + 0.5) / S * 2 * Math.PI;
			const x = Math.round(cx + radius * Math.cos(a));
		 const y = Math.round(cy + radius * Math.sin(a));
			let total = 0, cnt = 0;
			for (let dx = -1; dx <= 1; dx += 1) {
				for (let dy = -1; dy <= 1; dy += 1) {
					const sx = Math.max(0, Math.min(width - 1, x + dx));
					const sy = Math.max(0, Math.min(height - 1, y + dy));
					total += await reader(sx, sy);
					cnt += 1;
				}
			}
			lumList[s] = total / cnt;
		}
		const sorted = lumList.slice().sort((a,b)=>a-b);
		const median = sorted[Math.floor(sorted.length/2)];
		const mean = sorted.reduce((a,b)=>a+b,0)/sorted.length;
		const thr = isFinite(median) ? median : mean;
		mat.push(lumList.map(v => (v < thr ? 1 : 0)));
	}
	return { mat, sectors };
}

// export async function decodeSunCodeStructuredWithReader(reader, width = 300, height = 300, center = null) {
// 	const w = width, h = height;
// 	const baseCx = center ? center.x : w/2, baseCy = center ? center.y : h/2;
// 	const maxR = Math.min(w,h)/2 * 0.8;
// 	const testConfigs = [
// 		{ rings: 6, sectors: (r) => 32 + 4 * r },
// 		{ rings: 8, sectors: (r) => 36 + 4 * r },
// 		{ rings: 4, sectors: (r) => 28 + 4 * r }
// 	];
// 	const centers = [
// 		{ x: baseCx, y: baseCy },
// 		{ x: baseCx + 4, y: baseCy },
// 		{ x: baseCx - 4, y: baseCy },
// 		{ x: baseCx, y: baseCy + 4 },
// 		{ x: baseCx, y: baseCy - 4 },
// 	];

// 	for (const c of centers) {
// 		for (const cfg of testConfigs) {
// 			try {
// 				const { mat, sectors } = await sampleRingMatrixWithReader(reader, w, h, c.x, c.y, maxR, cfg);
// 				for (let mask = 0; mask < 8; mask += 1) {
// 					const unmasked = unmaskMatrix(mat, mask);
// 					const bits = linearizeBitsFromMatrix(unmasked, sectors);
// 					const parsed = parseHeaderAndPayload(bits);
// 					if (!parsed) continue;
// 					const bytes = bitsToBytes(parsed.payload);
// 					const dec = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
// 					const text = dec ? dec.decode(bytes) : _polyfillUtf8Decode(bytes);
// 					if (text && text.length && isValidText(text)) {
// 						return text.trim();
// 					}
// 				}
// 			} catch (e) {
// 				// try other center/config
// 			}
// 		}
// 	}
// 	return '';
// }
function otsuThreshold(values) {
	// values: array of luminance [0..255+]（这里范围实际~[0..255]）
	const hist = new Array(256).fill(0);
	const clamp = (v)=>Math.max(0, Math.min(255, Math.round(v)));
	for (const v of values) hist[clamp(v)]++;
	const total = values.length;
	let sum = 0; for (let t = 0; t < 256; t++) sum += t * hist[t];
	let sumB = 0, wB = 0, wF = 0, varMax = -1, threshold = 128;
	for (let t = 0; t < 256; t++) {
		wB += hist[t]; if (wB === 0) continue;
		wF = total - wB; if (wF === 0) break;
		sumB += t * hist[t];
		const mB = sumB / wB;
		const mF = (sum - sumB) / wF;
		const between = wB * wF * (mB - mF) * (mB - mF);
		if (between > varMax) { varMax = between; threshold = t; }
	}
	return threshold;
}

async function sampleRingMatrixAdaptive(reader, width, height, cx, cy, maxR, config) {
	const { rings, sectors } = config;
	const ringWidth = maxR / (rings + 1);
	const mat = [];
	for (let r = 0; r < rings; r += 1) {
		const S = sectors(r);
		const inner = ringWidth * (r + 1);
		const nominal = inner + ringWidth * 0.5;
		// 在该环半径附近搜索最佳半径（-2..+2 像素）
		let bestRadius = nominal, bestScore = -Infinity, bestLum = null;
		for (let ro = -2; ro <= 2; ro++) {
			const radius = nominal + ro;
			const lumList = new Array(S);
			for (let s = 0; s < S; s += 1) {
				const a = (s + 0.5) / S * 2 * Math.PI;
				const x = Math.round(cx + radius * Math.cos(a));
				const y = Math.round(cy + radius * Math.sin(a));
				let total = 0, cnt = 0;
				for (let dx = -1; dx <= 1; dx++) {
					for (let dy = -1; dy <= 1; dy++) {
						const sx = Math.max(0, Math.min(width - 1, x + dx));
						const sy = Math.max(0, Math.min(height - 1, y + dy));
						total += await reader(sx, sy);
						cnt++;
					}
				}
				lumList[s] = total / cnt;
			}
			// 评价该半径：环上亮度的类间方差（近似用方差），越大越好
			const mean = lumList.reduce((a,b)=>a+b,0)/S;
			const variance = lumList.reduce((a,b)=>a+(b-mean)*(b-mean),0)/S;
			if (variance > bestScore) { bestScore = variance; bestRadius = radius; bestLum = lumList; }
		}
		// 按 Otsu 阈值分割，不可用时回退到中位数
		const lumList = bestLum;
		const thrOtsu = otsuThreshold(lumList);
		const sorted = lumList.slice().sort((a,b)=>a-b);
		const median = sorted[Math.floor(sorted.length/2)];
		const thr = isFinite(thrOtsu) ? thrOtsu : (isFinite(median) ? median : 128);
		mat.push(lumList.map(v => (v < thr ? 1 : 0)));
	}
	return { mat, sectors };
}

export async function decodeSunCodeStructuredWithReader(reader, width = 300, height = 300, center = null) {
	const w = width, h = height;
	const baseCx = center ? center.x : w/2, baseCy = center ? center.y : h/2;
	const maxR = Math.min(w,h)/2 * 0.8;
	const testConfigs = [
		{ rings: 6, sectors: (r) => 32 + 4 * r },
		{ rings: 8, sectors: (r) => 36 + 4 * r },
		{ rings: 4, sectors: (r) => 28 + 4 * r }
	];
	// 扩大中心搜索：9x9 网格，步长 4px，范围 ±16px
	const centers = [];
	for (let dx = -16; dx <= 16; dx += 4) {
		for (let dy = -16; dy <= 16; dy += 4) {
			centers.push({ x: baseCx + dx, y: baseCy + dy });
		}
	}
	// 优先尝试精确中心
	centers.unshift({ x: baseCx, y: baseCy });

	for (const c of centers) {
		for (const cfg of testConfigs) {
			try {
				const { mat, sectors } = await sampleRingMatrixAdaptive(reader, w, h, c.x, c.y, maxR, cfg);
				for (let mask = 0; mask < 8; mask += 1) {
					const unmasked = unmaskMatrix(mat, mask);
					const bits = linearizeBitsFromMatrix(unmasked, sectors);
					const parsed = parseHeaderAndPayload(bits);
					if (!parsed) continue;
					const bytes = bitsToBytes(parsed.payload);
					const dec = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
					const text = dec ? dec.decode(bytes) : _polyfillUtf8Decode(bytes);
					if (text && text.length && isValidText(text)) {
						return text.trim();
					}
				}
			} catch (e) {
				// 尝试下一个中心/配置
			}
		}
	}
	return '';
}


